//
//  ZYImageOperation.m
//  ZYWebImage
//
//  Created by wangzhipan on 2025/3/5.
//

#import "ZYImageOperation.h"
#import <pthread/pthread.h>
#import "ZYWebImageConst.h"
#import "IZYDecoder.h"
#import "ZYImageDecoderFactory.h"
#import "ZYImageDecodeMgr.h"
#import "ZYImageOperationToken.h"
#import "UIImage+MetaData.h"
#import "ZYWebImageConst.h"


@interface ZYImageOperation()
@property (nonatomic, strong, readwrite, nullable) NSString *urlStr;
@property (nonatomic, copy, nullable) NSMutableArray <ZYImageOperationToken *>* callbacks;

@property (nonatomic, assign,readwrite, getter=isExecuting) BOOL executing;
@property (nonatomic, assign,readwrite, getter=isFinished) BOOL finished;
@property (nonatomic, assign,readwrite, getter=isCancelled) BOOL cancelled;

@property (nonatomic, strong, nullable) NSURLSessionDataTask *task;

//当没有上一级调用传来的session时，自行创建；有的话就复用上一级传来的

@property (nonatomic, strong) NSURLSession *ownedSession;
/*上一级调用传来复用的session
 复用上一个NSURLSession的好处：当创建session传入的queue是nil时，会默认创建串行队列
 这样任务的下载回调处理是串行的，不会因为并发导致一堆数据竞争问题
 */
@property (nonatomic, weak) NSURLSession *unownedSession;
@end



@implementation ZYImageOperation
@synthesize executing = _executing;
@synthesize finished = _finished;
@synthesize cancelled = _cancelled;
- (instancetype)initWithUrl:(nonnull NSString *)urlStr
                    session:(nullable NSURLSession *)session
{
    if (self = [super init]) {
        self.urlStr = urlStr;
        self.unownedSession = session;
        self.callbacks = [NSMutableArray array];
    }
    return self;
}

- (void)start
{
    @synchronized (self) {
        if (self.isCancelled) {
            [self p_doCompletion];
            return;
        }
        self.executing = YES;
    }
    
    /*
     先复用session，木有的话就直接创建一个自己的
     */
    NSURLSession *session = self.unownedSession;
    if (!session) {
        NSURLSessionConfiguration *sessionCfg = [NSURLSessionConfiguration defaultSessionConfiguration];
        sessionCfg.timeoutIntervalForRequest = 15;
        session = [NSURLSession sessionWithConfiguration:sessionCfg];
        self.ownedSession = session;
    }
    
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.urlStr]
                                                         cachePolicy:NSURLRequestReloadRevalidatingCacheData
                                                     timeoutInterval:15];
    
    [request setValue:[self p_userAgent] forHTTPHeaderField:@"User-Agent"];
    [request setValue:@"Accept" forHTTPHeaderField:@"image/*,*/*;q=0.8"];
    
    self.task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            [self p_doCallback:nil imageData:nil error:error];
            [self p_doCompletion];
            return;
        }
        @synchronized (self) {
            if (self.cancelled) {
                [self p_doCompletion];
                return;
            }
        }
        
        //获取对应的decoder
        id<IZYDecoder> decoder = [ZYImageDecoderFactory createDecoderForData:data];
        
        //异步串行解码
        [[ZYImageDecodeMgr sharedInstance] addBlockToDo:^{
            @synchronized (self) {
                if (self.cancelled) {
                    [self p_doCompletion];
                    return;
                }
            }
            
            UIImage *image = [decoder decodeImage];
            @synchronized (self) {
                if (self.cancelled) {
                    [self p_doCompletion];
                    return;
                }
            }
            //把下载好的原始数据保存在UIImage中，当store Image时，可以直接把原生data取出来存储，优化性能
            image.zy_metaData = data;
            [self p_doCallback:image imageData:data error:nil];
            [self p_doCompletion];
        }];
    }];
    
    [self.task resume];
}

- (ZYImageOperationToken *)addCompletedBlock:(ZYDownloadCompleted)completed
{
    ZYImageOperationToken *token = [[ZYImageOperationToken alloc] init];
    token.completed = completed;
    token.urlStr = self.urlStr;
    @synchronized (self) {
        [self.callbacks addObject:token];
    }
    return token;
}

- (void)cancelSingle:(ZYImageOperationToken *)token
{
    if (!token) {
        return;
    }
    BOOL shouldCancel = NO;
    @synchronized (self.callbacks) {
        if (self.callbacks.count == 1 && [self.callbacks indexOfObject:token] != NSNotFound) {
            shouldCancel = YES;
        }
    }
    if (shouldCancel) {
        [self cancel];
    }
    else {
        @synchronized (self.callbacks) {
            [self.callbacks removeObject:token];
        }
        [self p_callCompletedBlockWithToken:token
                                      image:nil
                                  imageData:nil
                                      error:[NSError errorWithDomain:@"ZYImageOperation: Operation cancelled by user during sending the request" code:ZYWebImageErrorCancelled userInfo:nil]];
    }
}

- (void)cancel
{
    @synchronized (self) {
        if (self.finished) {
            return;
        }
        [super cancel];
    }
    
    [self.task cancel];
    
    [self p_doCompletion];
}



- (void)p_doCompletion
{
    @synchronized (self) {
        self.executing = NO;
        self.finished = YES;
    }
    [self p_reset];
}

- (void)p_reset
{
    @synchronized (self) {
        self.task = nil;
        [self.ownedSession invalidateAndCancel];
        self.ownedSession = nil;
        [self.callbacks removeAllObjects];
    }
}

- (void)p_doCallback:(UIImage * _Nullable)image
           imageData:(NSData * _Nullable)imageData
               error:(NSError * _Nullable)error
{
    SafeAsyncMainThread(^{
        for (ZYImageOperationToken *token in self.callbacks) {
            token.completed(image, imageData ,error);
        }
    });
}

- (void)p_callCompletedBlockWithToken:(ZYImageOperationToken *)token
                                image:(UIImage *)image
                            imageData:(NSData *)imageData
                                error:(NSError *)error
{
    if (token.completed) {
        SafeAsyncMainThread(^{
            token.completed(image, imageData,error);
        });
    }
}

- (NSString *)p_userAgent
{
    NSString *userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
    
    if (userAgent) {
        if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
            NSMutableString *mutableUserAgent = [userAgent mutableCopy];
            if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                userAgent = mutableUserAgent;
            }
        }
    }
    return userAgent;
}

#pragma mark - kvo
- (void)setFinished:(BOOL)finished
{
    [self willChangeValueForKey:@"isFinished"];
    _finished = finished;
    [self didChangeValueForKey:@"isFinished"];
    
}


- (void)setExecuting:(BOOL)executing
{
    [self willChangeValueForKey:@"isExecuting"];
    _executing = executing;
    [self didChangeValueForKey:@"isExecuting"];
}


- (BOOL)isAsynchronous
{
    return YES;
}


@end
